package com.platform.repository.redis.impl;


import com.platform.domain.repository.redis.JedisRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Tuple;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;

/**
 * 集群用法 是一个大应用 为防止key冲突
 * 每个应用 独立一个code
 */
@Service
public class JedisRepositoryImpl implements JedisRepository {
    public Logger log = LoggerFactory.getLogger(this.getClass());
    //TODO 使用时放开
   // @Autowired
    JedisCluster jedisCluster ;

    @Value("${spring.application.code}")
    String app_code;

    private static final int DEFAULT_SINGLE_EXPIRE_TIME = 10;

    @Override
    public int set(String key, Object value) {
        return set(key, value, 30, TimeUnit.DAYS);
    }

    @Override
    public int set(String key, Object value, long timeout, TimeUnit timeUnit) {
        int isOk = 0;

        if (key == null) {
            return isOk;
        }

        String _key = getAppKey(key);

        try {
            jedisCluster.setex(_key, (int) TimeoutUtils.toSeconds(timeout, timeUnit), value.toString());
            isOk = 1;
        } catch (Exception e) {
            log.error( "set error redis key is : " + _key + " value : " + value+e.getMessage());
        }
        return isOk;
    }

    @Override
    public Object get(String key) {
        if (key == null){
            return null;
        }
        String _key = getAppKey(key);

        Object value = null;
        try {
            value = jedisCluster.get(_key);
        } catch (Exception e) {
            log.error( "redis key is : " + _key);
        }
        return value;
    }

    @Override
    public String getAsString(String key) {
        Object value = get(key);
        return (value == null) ? null : value.toString();
    }

    @Override
    public int remove(String key) {
        int isOk = 0;

        if (key == null){
            return isOk;
        }


        String _key = getAppKey(key);

        try {
            jedisCluster.del(_key);
            isOk = 1;
        } catch (Exception e) {
            log.error( "redis key is : " + _key);
        }
        return isOk;
    }

    @Override
    public int removeWithoutAppkey(String key) {
        int isOk = 0;

        if (key == null){
            return isOk;
        }
        String _key = key;

        try {
            jedisCluster.del(_key);
            isOk = 1;
        } catch (Exception e) {
            log.error( "redis key is : " + _key);
        }
        return isOk;
    }

    /**
     * 包含 pattern 字符串的keys 全部删除
     *
     * @param pattern
     * @return
     */
    @Override
    public int removeKeys(String pattern) {
        int ret = 0;

        if (pattern == null || "*".equals(pattern)){
            return ret;
        }
        try {
//            Set<String> keys = _getKeys(pattern);
//            Long ret = jedisCluster.del(keys.toArray(new String[0]));

            ret = (int) this._removeKeys(pattern);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return ret;
    }

    @Override
    public boolean tryLock(String _key) {
        return tryLock(_key, 0L, null);
    }

    @Override
    public boolean tryLock(String _key, long acquireTimeout, TimeUnit unit) {
        String key = getAppKey(_key);
        try {
            long nano = System.nanoTime();
            do {
                log.debug("try lock key: " + key);
                Long i = jedisCluster.setnx(key, key);
                if (i == 1) {
                    jedisCluster.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);
                    log.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");
                    return Boolean.TRUE;
                } else { // 存在锁
                    if (log.isDebugEnabled()) {
                        String desc = jedisCluster.get(key);
                        log.debug("key: " + key + " locked by another business：" + desc);
                    }
                }
                if (acquireTimeout == 0) {
                    break;
                }
                Thread.sleep(300);
            } while ((System.nanoTime() - nano) < unit.toNanos(acquireTimeout));
            return Boolean.FALSE;
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return Boolean.FALSE;
    }

    @Override
    public boolean lock(String _key, long lockTimeout, TimeUnit unit) {
        String key = getAppKey(_key);
        try {
            do {
                log.debug("lock key: " + key);
                Long i = jedisCluster.setnx(key, key);
                if (i == 1) {
                    jedisCluster.expire(key, lockTimeout == 0 ? DEFAULT_SINGLE_EXPIRE_TIME : (int) unit.toSeconds(lockTimeout));
                    log.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");
                    return Boolean.TRUE;
                } else {
                    if (log.isDebugEnabled()) {
                        String desc = jedisCluster.get(key);
                        log.debug("key: " + key + " locked by another business：" + desc);
                    }
                }
                Thread.sleep(300);
            } while (true);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return Boolean.FALSE;
    }

    @Override
    public void unLock(String _key) {
        String key = getAppKey(_key);
        try {
            jedisCluster.del(key);
//            System.out.println("------"+jedisCluster.get(key));
            log.debug("release lock, keys :" + key);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    @Override
    public String hget(String _key, String field) {
        String key = getAppKey(_key);
        return jedisCluster.hget(key, field);
    }

    @Override
    public Long hlen(String _key) {
        String key = getAppKey(_key);
        return jedisCluster.hlen(key);
    }

    @Override
    public Boolean exists(String _key) {
        String key = getAppKey(_key);
        return jedisCluster.exists(key);
    }

    @Override
    public Long hset(String _key, String field, String value) {
        String key = getAppKey(_key);
        return jedisCluster.hset(key, field, value);
    }

    @Override
    public String hmset(String _key, Map<String, String> hash) {
        String key = getAppKey(_key);
        return jedisCluster.hmset(key, hash);
    }

    @Override
    public Long expire(String _key, int seconds) {
        String key = getAppKey(_key);
        return jedisCluster.expire(key, seconds);
    }

    @Override
    public List<String> hmget(String _key, String... fields) {
        String key = getAppKey(_key);
        return jedisCluster.hmget(key, fields);
    }

    @Override
    public Map<String, String> hgetAll(String _key) {
        String key = getAppKey(_key);
        return jedisCluster.hgetAll(key);
    }

    @Override
    public Long ttl(String key) {
        String _key = getAppKey(key);
        return jedisCluster.ttl(_key);
    }

    @Override
    public Long llen(String key) {
        String _key = getAppKey(key);
        return jedisCluster.llen(_key);
    }

    @Override
    public Long lpush(String key, String item) {
        String _key = getAppKey(key);
        return jedisCluster.lpush(_key, item);
    }

    @Override
    public String lpop(String key) {
        String _key = getAppKey(key);
        return jedisCluster.lpop(_key);
    }

    @Override
    public List<String> lrange(String key, int start, int end) {
        String _key = getAppKey(key);
        return jedisCluster.lrange(_key, start, end);
    }

    @Override
    public Long lpush(String key, String[] item) {
        String _key = getAppKey(key);
        return jedisCluster.lpush(_key, item);
    }

    @Override
    public Long sadd(String _key, String... member) {
        String key = getAppKey(_key);
        return jedisCluster.sadd(key, member);
    }

    @Override
    public Long srem(String _key, String... member) {
        String key = getAppKey(_key);
        return jedisCluster.srem(key, member);
    }

    @Override
    public Long hdel(String _key, String field) {
        String key = getAppKey(_key);
        return jedisCluster.hdel(key, field);
    }

    @Override
    public Long zadd(String _key, Integer score, String member) {

        String key = getAppKey(_key);
        return jedisCluster.zadd(key, score, member);
    }

    @Override
    public Long zadd(String _key, Double score, String member) {

        String key = getAppKey(_key);
        return jedisCluster.zadd(key, score, member);
    }

    @Override
    public Double zincrby(String _key, Integer increment, String member) {

        String key = getAppKey(_key);
        return jedisCluster.zincrby(key, increment, member);
    }

    @Override
    public Long zcard(String _key) {

        String key = getAppKey(_key);
        return jedisCluster.zcard(key);
    }

    @Override
    public Double zscore(String _key, String member) {

        String key = getAppKey(_key);
        return jedisCluster.zscore(key, member);
    }

    @Override
    public Long zrevrank(String _key, String member) {

        String key = getAppKey(_key);
        return jedisCluster.zrevrank(key, member);
    }

    @Override
    public Long zrem(String _key, String member) {

        String key = getAppKey(_key);
        return jedisCluster.zrem(key, member);
    }


    //保存指定范围的排名，用于榜单
    @Override
    public Long zremrangeByRank(String _key, long start, long end) {

        String key = getAppKey(_key);
        return jedisCluster.zremrangeByRank(key, start, end);
    }


    private String getAppKey(String key) {
        return app_code + "_" + key;
    }

    TreeSet<String> _getKeys(String pattern) {
        String _key = getAppKey(pattern);
        log.debug("Start getting keys...");
        TreeSet<String> keys = new TreeSet<String>();
        Map<String, JedisPool> clusterNodes = jedisCluster.getClusterNodes();
        for (String k : clusterNodes.keySet()) {
            log.debug("Getting keys from: {}", k);
            JedisPool jp = clusterNodes.get(k);
            Jedis jedis = jp.getResource();
            try {
                Set<String> _keys = jedis.keys("*" + _key + "*");
                keys.addAll(_keys);
            } catch (Exception e) {
                log.error("Getting keys error: {}", e);
            } finally {
                log.debug("Connection closed.");
                jedis.close();//用完一定要close这个链接！！！
            }
        }
        log.debug("Keys gotten!");
        return keys;
    }

    private long _removeKeys(String pattern) {
        String _key = getAppKey(pattern);
        long ret = 0l;
        log.debug("Start remove keys...");
        Map<String, JedisPool> clusterNodes = jedisCluster.getClusterNodes();
        for (String k : clusterNodes.keySet()) {
            log.debug("Getting keys from: {}", k);
            JedisPool jp = clusterNodes.get(k);
            Jedis jedis = jp.getResource();
            try {
                Set<String> keys = jedis.keys("*" + _key + "*");
                for (String _k : keys) {
                    ret += jedisCluster.del(_k);
                }
            } catch (Exception e) {
                log.error("Getting keys error: {}", e);
            } finally {
                log.debug("Connection closed.");
                jedis.close();//用完一定要close这个链接！！！
            }
        }
        log.debug("Keys gotten!");
        return ret;
    }

    @Override
    public long incr(String key) {
        String _key = getAppKey(key);
        return jedisCluster.incr(_key);
    }
}
